<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>气象异常预测模块</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        :root {
            --primary-blue: #1e88e5;
            --secondary-blue: #64b5f6;
            --accent-green: #4caf50;
            --accent-orange: #ff9800;
            --accent-red: #f44336;
            --dark-bg: #1a237e;
            --light-bg: #f5f7fa;
            --card-bg: #ffffff;
            --text-dark: #263238;
            --text-light: #78909c;
        }

        body {
            font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
            background: linear-gradient(135deg, #e3f2fd 0%, #f5f7fa 100%);
            color: var(--text-dark);
            line-height: 1.6;
            min-height: 100vh;
            padding: 0;
            margin: 0;
        }

        .container-fluid {
            padding: 0;
        }

        /* 顶部导航栏 */
        .top-nav {
            background: linear-gradient(90deg, var(--dark-bg) 0%, var(--primary-blue) 100%);
            color: white;
            padding: 0.8rem 0;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }

        .nav-brand {
            font-size: 1.5rem;
            font-weight: 600;
            display: flex;
            align-items: center;
        }

        .nav-brand i {
            margin-right: 10px;
            font-size: 1.8rem;
        }

        /* 控制面板 */
        .control-panel {
            background: var(--card-bg);
            border-radius: 12px;
            padding: 1.5rem;
            margin: 1.5rem 0;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
            border: 1px solid rgba(0, 0, 0, 0.05);
        }

        .date-range-picker {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .date-input {
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            padding: 0.6rem 1rem;
            font-size: 0.95rem;
            transition: all 0.3s;
        }

        .date-input:focus {
            border-color: var(--primary-blue);
            box-shadow: 0 0 0 3px rgba(30, 136, 229, 0.2);
        }

        .btn-custom {
            background: linear-gradient(135deg, var(--primary-blue) 0%, var(--secondary-blue) 100%);
            border: none;
            border-radius: 8px;
            padding: 0.7rem 1.5rem;
            color: white;
            font-weight: 500;
            transition: all 0.3s;
            box-shadow: 0 2px 5px rgba(30, 136, 229, 0.3);
        }

        .btn-custom:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(30, 136, 229, 0.4);
        }

        /* 统计卡片 */
        .stats-container {
            margin: 1.5rem 0;
        }

        .stat-card {
            background: var(--card-bg);
            border-radius: 12px;
            padding: 1.5rem;
            text-align: center;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
            border-left: 4px solid var(--primary-blue);
            transition: transform 0.3s;
            height: 100%;
        }

        .stat-card:hover {
            transform: translateY(-5px);
        }

        .stat-card.temperature {
            border-left-color: var(--accent-red);
        }

        .stat-card.anomaly {
            border-left-color: var(--accent-orange);
        }

        .stat-card.rate {
            border-left-color: var(--accent-green);
        }

        .stat-icon {
            font-size: 2.5rem;
            margin-bottom: 1rem;
            opacity: 0.8;
        }

        .stat-value {
            font-size: 2.2rem;
            font-weight: 700;
            margin: 0.5rem 0;
        }

        .stat-label {
            font-size: 1rem;
            color: var(--text-light);
            font-weight: 500;
        }

        /* 图表容器 */
        .chart-container {
            background: var(--card-bg);
            border-radius: 12px;
            padding: 1.5rem;
            margin-bottom: 1.5rem;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
            height: 400px;
            position: relative;
        }

        .chart-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 1.5rem;
        }

        .chart-title {
            font-size: 1.3rem;
            font-weight: 600;
            color: var(--text-dark);
            margin: 0;
        }

        /* 预测表格 */
        .prediction-table {
            background: var(--card-bg);
            border-radius: 12px;
            padding: 1.5rem;
            margin-bottom: 2rem;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
        }

        .table-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 1rem;
        }

        .table-custom {
            width: 100%;
            border-collapse: separate;
            border-spacing: 0;
        }

        .table-custom thead th {
            background: #f1f8fe;
            color: var(--text-dark);
            font-weight: 600;
            padding: 1rem;
            border-bottom: 2px solid #e3f2fd;
        }

        .table-custom tbody tr {
            transition: background 0.2s;
        }

        .table-custom tbody tr:hover {
            background: #f8fdff;
        }

        .table-custom tbody td {
            padding: 1rem;
            border-bottom: 1px solid #f0f0f0;
        }

        .anomaly-badge {
            padding: 0.3rem 0.8rem;
            border-radius: 20px;
            font-size: 0.85rem;
            font-weight: 500;
        }

        .anomaly-true {
            background: #ffebee;
            color: var(--accent-red);
        }

        .anomaly-false {
            background: #e8f5e9;
            color: var(--accent-green);
        }

        .reason-badge {
            padding: 0.3rem 0.6rem;
            border-radius: 12px;
            font-size: 0.75rem;
            margin-right: 0.3rem;
            margin-bottom: 0.3rem;
            display: inline-block;
        }

        .reason-temperature {
            background: #ffcdd2;
            color: #b71c1c;
        }

        .reason-humidity {
            background: #bbdefb;
            color: #0d47a1;
        }

        .reason-wind {
            background: #c8e6c9;
            color: #1b5e20;
        }

        .reason-pressure {
            background: #e1bee7;
            color: #4a148c;
        }

        .reason-multiple {
            background: #ffcc80;
            color: #e65100;
        }

        /* 加载状态 */
        .loading-spinner {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 3px solid #f3f3f3;
            border-top: 3px solid var(--primary-blue);
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin-right: 10px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        /* 响应式调整 */
        @media (max-width: 768px) {
            .chart-container {
                height: 300px;
            }

            .date-range-picker {
                flex-direction: column;
                gap: 15px;
            }

            .btn-group-custom {
                width: 100%;
            }

            .btn-custom {
                width: 100%;
                margin-bottom: 10px;
            }
        }
    </style>
</head>
<body>
<div class="container-fluid">
<!--    &lt;!&ndash; 顶部导航栏 &ndash;&gt;
    <div class="top-nav">
        <div class="container">
            <div class="d-flex justify-content-between align-items-center">
                <div class="nav-brand">
                    <i class="fas fa-cloud-sun"></i>
                    <span>气象异常预测</span>
                </div>
                <div class="text-end">
                    <span class="me-3"><span id="lastUpdateTime">&#45;&#45;</span></span>
                </div>
            </div>
        </div>
    </div>-->

    <!-- 主内容区 -->
    <div class="container mt-4">
        <!-- 控制面板 -->
        <div class="control-panel">
            <div class="row align-items-center">
                <div class="col-md-8">
                    <div class="date-range-picker">
                        <input type="date" class="date-input" id="startDate" value="2025-01-09">
                        <span>至</span>
                        <input type="date" class="date-input" id="endDate" value="2025-09-01">
                    </div>
                </div>
                <div class="col-md-4">
                    <div class="d-flex gap-2 justify-content-end">
                        <button class="btn btn-custom" id="loadData">
                            <i class="fas fa-search me-1"></i> 查询数据
                        </button>
                    </div>
                </div>
            </div>
        </div>

        <!-- 统计卡片 -->
        <div class="row stats-container">
            <div class="col-4 mb-3">
                <div class="stat-card temperature">
                    <i class="fas fa-thermometer-half stat-icon" style="color: var(--accent-red);"></i>
                    <div class="stat-value" id="avgTemperature">--</div>
                    <div class="stat-label">平均温度</div>
                </div>
            </div>
            <div class="col-4 mb-3">
                <div class="stat-card anomaly">
                    <i class="fas fa-exclamation-triangle stat-icon" style="color: var(--accent-orange);"></i>
                    <div class="stat-value" id="abnormalCount">--</div>
                    <div class="stat-label">异常事件</div>
                </div>
            </div>
            <div class="col-4 mb-3">
                <div class="stat-card rate">
                    <i class="fas fa-chart-pie stat-icon" style="color: var(--accent-green);"></i>
                    <div class="stat-value" id="abnormalRate">--</div>
                    <div class="stat-label">异常比例</div>
                </div>
            </div>
        </div>

        <!-- 图表区域 -->
        <div class="chart-container">
            <div class="chart-header">
                <h3 class="chart-title">异常分布趋势</h3>
                <div class="chart-legend">
                   <!-- <span class="me-3"><i class="fas fa-circle me-1" style="color: #1e88e5;"></i> 正常</span>
                    <span><i class="fas fa-circle me-1" style="color: #f44336;"></i> 异常</span>-->
                </div>
            </div>
            <canvas id="anomalyDistributionChart"></canvas>
        </div>

        <!-- 下一周预测 -->
        <div class="prediction-table">
            <div class="table-header">
                <h3 class="chart-title">下一周预测</h3>
                <div>
                    <span class="badge bg-light text-dark" id="predictionCount">7 天预测</span>
                </div>
            </div>
            <div class="table-responsive">
                <table class="table table-custom">
                    <thead>
                    <tr>
                        <th>日期</th>
                        <th>预测温度</th>
                        <!--<th>湿度</th>-->
                        <th>风速</th>
                       <!-- <th>气压</th>-->
                        <th>预测结果</th>
                        <!--<th>置信度</th>-->
                        <th>风险等级</th>
                        <th>异常原因</th>
                    </tr>
                    </thead>
                    <tbody id="predictionBody">
                    <!-- 预测数据将通过JavaScript动态加载 -->
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>

<script>
    // 全局变量
    window.anomalyDistributionChart = null;
    // 异常类型-颜色映射表（关键词匹配）
    const anomalyColorMap = {
        '高温': '#ff4444',    // 红色
        '低温': '#4285f4',    // 蓝色
        '大风': '#ffc107',    // 黄色
        '暴雨': '#00acc1',    // 青色
        '暴雪': '#e3f2fd',    // 浅蓝
    };
    //辅助标记颜色方法
    function mixColors(colors) {
        if (colors.length === 1) return colors[0]; // 单个颜色直接返回

        let r = 0, g = 0, b = 0;
        colors.forEach(color => {
            // 解析16进制颜色为RGB（如 #ff4444 → r=255, g=68, b=68）
            const hex = color.replace('#', '');
            r += parseInt(hex.substring(0, 2), 16);
            g += parseInt(hex.substring(2, 4), 16);
            b += parseInt(hex.substring(4, 6), 16);
        });

        // 取平均值并转换回16进制
        r = Math.round(r / colors.length).toString(16).padStart(2, '0');
        g = Math.round(g / colors.length).toString(16).padStart(2, '0');
        b = Math.round(b / colors.length).toString(16).padStart(2, '0');
        return `#${r}${g}${b}`;
    }
    // 页面加载完成后初始化
    document.addEventListener('DOMContentLoaded', function() {
        // 绑定加载数据按钮事件
        document.getElementById('loadData').addEventListener('click', loadData);

        // 设置默认日期为当前日期和一周后
       /* const today = new Date();
        const nextWeek = new Date();
        nextWeek.setDate(today.getDate() + 7);

        document.getElementById('startDate').value = formatDate(today);
        document.getElementById('endDate').value = formatDate(nextWeek);*/

        // 初始加载数据
       // loadData();
    });

    // 格式化日期为YYYY-MM-DD
    function formatDate(date) {
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0');
        const day = String(date.getDate()).padStart(2, '0');
        return `${year}-${month}-${day}`;
    }

    // 计算下一周日期范围
    function getNextWeekDates(endDate) {
        const end = new Date(endDate);
        const nextWeekStart = new Date(end);
        nextWeekStart.setDate(end.getDate() + 1);

        const nextWeekEnd = new Date(nextWeekStart);
        nextWeekEnd.setDate(nextWeekStart.getDate() + 6);

        return {
            start: formatDate(nextWeekStart),
            end: formatDate(nextWeekEnd)
        };
    }

    // 加载数据函数
    async function loadData() {
        const startDate = document.getElementById('startDate').value;
        const endDate = document.getElementById('endDate').value;
        // 生成选中时间范围内的所有日期（如2025-01-10 ~ 2025-01-16）
        const allDates = generateAllDates(startDate, endDate);
        // 1. 强制校验日期（必须选择）
        if (!startDate || !endDate) {
            alert('请选择完整的开始和结束日期！');
            return;
        }
        if (new Date(startDate) > new Date(endDate)) {
            alert('开始日期不能晚于结束日期！');
            return;
        }

        // 2. 预测数据的时间范围与统计数据一致（删除错误的 getNextWeekDates 逻辑）
        const predictStart = startDate;
        const predictEnd = endDate;
        // 计算下一周日期范围

        const nextWeekDates = getNextWeekDates(endDate);

        // 显示加载状态
        showLoadingState();

        try {
            // 获取统计信息
            const statsResponse = await fetch(`/api/weather/anomaly/stats?startDate=${startDate}&endDate=${endDate}`);
            if (!statsResponse.ok) {
                throw new Error(`统计信息请求失败: ${statsResponse.status}`);
            }
            const statsData = await statsResponse.json();

            // 获取预测数据
            const predictResponse = await fetch(`/api/weather/anomaly/predict?startDate=${predictEnd}`);
            if (!predictResponse.ok) {
                throw new Error(`预测数据请求失败: ${predictResponse.status}`);
            }
            const predictData = await predictResponse.json();

            // 更新界面
            updateStatsDisplay(statsData);
            updateChart(statsData,allDates);
            updatePredictionTable(predictData);
            updateLastUpdateTime();

        } catch (error) {
            console.error('数据加载失败:', error);
            alert('数据加载失败或无此数据');
        } finally {
            // 隐藏加载状态
            hideLoadingState();
        }
    }
    // 辅助函数：生成两个日期之间的所有日期（含首尾）
    function generateAllDates(startStr, endStr) {
        const start = new Date(startStr);
        const end = new Date(endStr);
        const dates = [];
        let current = new Date(start);
        while (current <= end) {
            dates.push(current.toISOString().split('T')[0]); // 格式：YYYY-MM-DD
            current.setDate(current.getDate() + 1);
        }
        return dates;
    }

    // 显示加载状态
    function showLoadingState() {
        const loadButton = document.getElementById('loadData');
        loadButton.innerHTML = '<div class="loading-spinner"></div> 加载中...';
        loadButton.disabled = true;

        document.getElementById('avgTemperature').textContent = '加载中...';
        document.getElementById('abnormalCount').textContent = '加载中...';
        document.getElementById('abnormalRate').textContent = '加载中...';

        document.getElementById('predictionBody').innerHTML = `
            <tr>
                <td colspan="9" class="text-center text-muted">正在加载数据...</td>
            </tr>
        `;
    }

    // 隐藏加载状态
    function hideLoadingState() {
        const loadButton = document.getElementById('loadData');
        loadButton.innerHTML = '<i class="fas fa-search me-1"></i> 查询数据';
        loadButton.disabled = false;
    }

    // 更新统计信息显示
    function updateStatsDisplay(stats) {
        document.getElementById('avgTemperature').textContent = `${stats.avgTemperature.toFixed(1)}°C`;
        document.getElementById('abnormalCount').textContent = stats.abnormalCount;
        document.getElementById('abnormalRate').textContent = `${(stats.abnormalRate*100).toFixed(1)}%`;
    }


    function updateChart(statsData, allDates) {
        const ctx = document.getElementById('anomalyDistributionChart').getContext('2d');

        // 销毁旧图表（安全检查）
        if (window.anomalyDistributionChart && typeof window.anomalyDistributionChart.destroy === 'function') {
            window.anomalyDistributionChart.destroy();
        }

        // 步骤1：构建异常原因映射（日期 → 异常类型列表）
        const anomalyReasonMap = new Map();
        (statsData.abnormalDetails || []).forEach(item => {
            const date = item.date;
            if (!anomalyReasonMap.has(date)) {
                anomalyReasonMap.set(date, []);
            }

            // 拆分复合异常原因（按“；”分割）
            const reasonStr = item.reason || '';
            const subReasons = reasonStr.split('；')
                .map(r => r.trim())
                .filter(r => r !== '');

            // 逐个匹配异常类型
            subReasons.forEach(subReason => {
                const matchedType = Object.keys(anomalyColorMap).find(type => subReason.includes(type));
                if (matchedType) {
                    anomalyReasonMap.get(date).push(matchedType);
                } else {
                    anomalyReasonMap.get(date).push('未知');
                    console.warn('未匹配异常类型:', subReason);
                }
            });
        });

        // 步骤2：生成图表数据（含颜色、异常类型）
        const chartData = allDates.map(date => {
            const reasons = anomalyReasonMap.get(date) || [];
            return {
                date: date,
                hasAnomaly: reasons.length > 0,
                reasons: reasons,
                color: reasons.length === 0
                    ? '#1e88e5' // 正常颜色（蓝色）
                    : mixColors(reasons.map(type => anomalyColorMap[type] || '#666')) // 异常混合色
            };
        });

        // 步骤3：提取图表所需数据（解决 labels 未定义问题）
        const labels = chartData.map(item => item.date.substring(5)); // 提取“月-日”作为X轴标签
        const data = chartData.map(item => item.hasAnomaly ? 1 : 0); // 异常标记（1=异常，0=正常）
        const backgroundColors = chartData.map(item => item.color); // 柱子颜色

        // 步骤4：渲染Chart.js图表
        window.anomalyDistributionChart = new Chart(ctx, {
            type: 'bar',
            data: {
                labels: labels, // ✅ 此处 labels 已正确定义
                datasets: [{
                    label: '异常状态',
                    data: data,
                    backgroundColor: backgroundColors,
                    borderRadius: 5
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    x: {
                        grid: { display: false },
                        title: { display: true, text: '日期' }
                    },
                    y: {
                        display: false, // 隐藏Y轴（因数据只有0/1，无实际意义）
                        beginAtZero: true
                    }
                },
                plugins: {
                    legend: { display: false }, // 单数据集，隐藏图例
                    tooltip: {
                        callbacks: {
                            title: (context) => `日期：${allDates[context[0].dataIndex]}`, // 完整日期
                            label: (context) => {
                                const item = chartData[context.dataIndex];
                                return item.hasAnomaly
                                    ? `异常类型：${item.reasons.join(' + ')}`
                                    : '正常（无异常）';
                            }
                        }
                    }
                }
            }
        });
    }

    // 更新预测表格
    function updatePredictionTable(predictions) {
        const predictionBody = document.getElementById('predictionBody');
        const predictionCount = document.getElementById('predictionCount');

        // 清空表格
        predictionBody.innerHTML = '';

        // 更新预测数量
        predictionCount.textContent = `${predictions.length} 条预测`;

        // 添加预测数据行
        predictions.forEach(prediction => {
            const row = document.createElement('tr');
            const dateTime = prediction.time.split(' ');
            const date = dateTime[0];
            const time = dateTime[1] || '';

            // 生成异常原因标签
            let reasonBadges = '';
            if (prediction.isAbnormal === 1 && prediction.anomalyReason) {
                const reasons = parseAnomalyReasons(prediction.anomalyReason);
                reasons.forEach(reason => {
                    const badgeClass = getReasonBadgeClass(reason);
                    reasonBadges += `<span class="reason-badge ${badgeClass}">${reason}</span>`;
                });
            } else if (prediction.isAbnormal === 1) {
                reasonBadges = '<span class="reason-badge reason-multiple">多因素异常</span>';
            } else {
                reasonBadges = '<span class="text-muted">-</span>';
            }

            // 风险等级标签
            const riskLevelClass = getRiskLevelClass(prediction.riskLevel);

            row.innerHTML = `
                <td>${date}<br><small class="text-muted">${time}</small></td>
                <td>${prediction.temperature.toFixed(1)}°C</td>
               <!-- <td>${prediction.humidity.toFixed(1)}%</td> -->
                <td>${prediction.windSpeed.toFixed(1)} m/s</td>
                <!--/*<td>${prediction.pressure.toFixed(1)} hPa</td>*/-->
                <td>
                    <span class="anomaly-badge ${prediction.isAbnormal === 1 ? 'anomaly-true' : 'anomaly-false'}">
                        ${prediction.isAbnormal === 1 ? '异常' : '正常'}
                    </span>
                </td>
                <!--<td>${prediction.confidence.toFixed(1)}%</td>-->
                <td>
                    <span class="badge ${riskLevelClass}">
                        ${getRiskLevelText(prediction.riskLevel)}
                    </span>
                </td>
                <td>${reasonBadges}</td>
            `;

            predictionBody.appendChild(row);
        });
    }

    // 解析异常原因字符串
    function parseAnomalyReasons(reasonString) {
        if (!reasonString || reasonString === "气象条件正常，无异常风险") {
            return [];
        }

        // 按逗号分割，但排除建议关注部分
        const reasons = reasonString.split('，').filter(reason =>
            !reason.includes('建议关注') && reason.trim() !== ''
        );

        return reasons.length > 0 ? reasons : ['多因素综合异常'];
    }

    // 获取异常原因对应的CSS类
    function getReasonBadgeClass(reason) {
        if (reason.includes('温度') || reason.includes('高温') || reason.includes('低温')) {
            return 'reason-temperature';
        } else if (reason.includes('湿度')) {
            return 'reason-humidity';
        } else if (reason.includes('风')) {
            return 'reason-wind';
        } else if (reason.includes('气压')) {
            return 'reason-pressure';
        } else {
            return 'reason-multiple';
        }
    }

    // 获取风险等级对应的CSS类
    function getRiskLevelClass(riskLevel) {
        switch (riskLevel) {
            case 'high': return 'bg-danger';
            case 'medium': return 'bg-warning';
            case 'low': return 'bg-success';
            default: return 'bg-secondary';
        }
    }

    // 获取风险等级文本
    function getRiskLevelText(riskLevel) {
        switch (riskLevel) {
            case 'high': return '高风险';
            case 'medium': return '中风险';
            case 'low': return '低风险';
            default: return '未知';
        }
    }

    // 更新最后更新时间
    function updateLastUpdateTime() {
        const now = new Date();
        const el = document.getElementById('lastUpdateTime');
        if (el) { // 仅当元素存在时，才设置内容
            el.textContent =
                `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
        }

    }
</script>
</body>
</html>
